﻿using Dyao.Book.Contract;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Dyao.Book.Application
{
    public static partial class TaskCenterManagerExtension
    {
        /// <summary>
        /// 创建任务中心管理器
        /// </summary>
        /// <typeparam name="TResult">结果类型</typeparam>
        /// <typeparam name="TExcutingInfo">执行中信息类型</typeparam>
        /// <param name="serviceProvider">serviceProvider</param>
        /// <param name="taskCenterModel">任务中心参数</param>
        /// <param name="isDistributedTask">是否分布式任务,默认为true</param>
        /// <param name="cancellationToken">任务取消令牌</param>
        /// <returns></returns>
        public static TaskCenterManager<TResult, TExcutingInfo> CreateTaskCenterManager<TResult, TExcutingInfo>(this IServiceProvider serviceProvider,
            TaskCenterDto taskCenterModel,
            bool isDistributedTask = true,
            CancellationToken cancellationToken = default)
            where TExcutingInfo : class, new()
        {
            return serviceProvider.GetService<TaskCenterManagerFactory>()
                .Create<TaskCenterManager<TResult, TExcutingInfo>>(taskCenterModel, isDistributedTask, cancellationToken);
        }

        public static TaskCenterManager<TResult> CreateTaskCenterManager<TResult>(this IServiceProvider serviceProvider,
            TaskCenterDto taskCenterModel,
            bool isDistributedTask = false,
            CancellationToken cancellationToken = default)
        {
            return serviceProvider.GetService<TaskCenterManagerFactory>()
                 .Create<TaskCenterManager<TResult>>(taskCenterModel, isDistributedTask, cancellationToken);
        }

        public static TaskCenterManager CreateTaskCenterManager(this IServiceProvider serviceProvider,
            TaskCenterDto taskCenterModel,
            bool isDistributedTask = false,
            CancellationToken cancellationToken = default)
        {
            return serviceProvider.GetService<TaskCenterManagerFactory>()
                 .Create<TaskCenterManager>(taskCenterModel, isDistributedTask, cancellationToken);
        }

        /// <summary>
        /// 开始执行任务中心的任务。
        /// 注意：该方法只适用于非分布式任务（任务所有操作一次性执行完成，不分发子任务）。
        /// 分布式任务不允许调用该方法执行，否则会导致子任务未完成，而任务记录已完成等错误。
        /// </summary>
        /// <param name="serviceProvider">serviceProvider</param>
        /// <param name="action">业务操作</param>
        /// <param name="taskCenterRecordId">任务记录id</param>
        /// <param name="currentRetryCount">当前重试次数</param>
        /// <param name="cancellationToken">cancellationToken</param>
        /// <returns></returns>
        public static Task BeginSingleTaskAsync(this IServiceProvider serviceProvider,
            Func<TaskCenterSingleContext, Task> action,
            TaskCenterDto taskCenterModel,
            CancellationToken cancellationToken = default)
        {
            return BeginSingleTaskAsync(serviceProvider, action, null, taskCenterModel, cancellationToken);
        }

        /// <summary>
        /// 开始执行任务中心的任务。
        /// 注意：该方法只适用于非分布式任务（任务所有操作一次性执行完成，不分发子任务）。
        /// 分布式任务不允许调用该方法执行，否则会导致子任务未完成，而任务记录已完成等异常。
        /// </summary>
        /// <param name="serviceProvider">serviceProvider</param>
        /// <param name="action">业务操作</param>
        /// <param name="failMessageFormatter">出错时消息格式化</param>
        /// <param name="taskCenterRecordId">任务记录id</param>
        /// <param name="currentRetryCount">当前重试次数</param>
        /// <param name="cancellationToken">cancellationToken</param>
        /// <returns></returns>
        public static async Task BeginSingleTaskAsync(this IServiceProvider serviceProvider,
            Func<TaskCenterSingleContext, Task> action,
            Func<Exception, string> failMessageFormatter,
            TaskCenterDto taskCenterModel,
            CancellationToken cancellationToken = default)
        {
            if (taskCenterModel is null)
            {
                throw new ArgumentNullException(nameof(taskCenterModel));
            }
            var taskCenterRecordId = taskCenterModel.TaskCenterRecordId;
            var currentRetryCount = taskCenterModel.CurrentRetryCount;

            var manager = serviceProvider.CreateTaskCenterManager(taskCenterModel, isDistributedTask: false, cancellationToken: cancellationToken);
            var context = new TaskCenterSingleContext(taskCenterModel, serviceProvider);

            context.OnUpdate = () =>
            {
                return manager.UpdateProgressAsync(model =>
                {
                    model.ExcutingProgress = context.ExcutingProgress;

                    if (!string.IsNullOrWhiteSpace(context.ResultMessage))
                    {
                        model.ResultMessage = context.ResultMessage;
                    }

                }, false);
            };
            context.SetLoadRequestParams(manager.GetRequestParamsAsync);

            try
            {
                await manager.StartAsync();

                await action(context);

                await manager.SuccessAsync(resultMessage: context.ResultMessage);

            }
            catch (Exception ex)
            {
                var logger = serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger("TaskCenterManager");
                var dict = new Dictionary<string, object>()
                {
                    { "recordId",taskCenterRecordId},
                    { "currentRetryCount",currentRetryCount}
                };

                using (var scope = logger.BeginScope(dict))
                {
                    logger.LogError(ex, $"任务id:{taskCenterRecordId},执行失败:{ex.Message}");
                }

                string message = failMessageFormatter?.Invoke(ex);

                try
                {

                    await manager.FailAsync(ex, message);
                }
                catch (Exception fex)
                {
                    using (var fscope = logger.BeginScope(dict))
                    {
                        logger.LogError(fex, $"任务id:{taskCenterRecordId},执行更新失败信息时失败:{fex.Message}");
                    }
                }

                //throw;
            }
        }

        /// <summary>
        /// 开始执行任务中心的任务。
        /// 注意：该方法只适用于非分布式任务（任务所有操作一次性执行完成，不分发子任务）。
        /// 分布式任务不允许调用该方法执行，否则会导致子任务未完成，而任务记录已完成等异常。
        /// </summary>
        /// <typeparam name="TResult">执行结果类型</typeparam>
        /// <param name="serviceProvider"></param>
        /// <param name="func">业务操作</param>
        /// <param name="failMessageFormatter"></param>
        /// <param name="taskCenterModel">任务记录</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static Task BeginSingleTaskAsync<TResult>(this IServiceProvider serviceProvider,
            Func<TaskCenterSingleContext, Task<TResult>> func,
            TaskCenterDto taskCenterModel,
            CancellationToken cancellationToken = default)
        {
            return BeginSingleTaskAsync(serviceProvider, func, null, taskCenterModel, cancellationToken);
        }

        /// <summary>
        /// 开始执行任务中心的任务。
        /// 注意：该方法只适用于非分布式任务（任务所有操作一次性执行完成，不分发子任务）。
        /// 分布式任务不允许调用该方法执行，否则会导致子任务未完成，而任务记录已完成等异常。
        /// </summary>
        /// <typeparam name="TResult">执行结果类型</typeparam>
        /// <param name="serviceProvider"></param>
        /// <param name="func">业务操作</param>
        /// <param name="failMessageFormatter"></param>
        /// <param name="taskCenterModel">任务记录</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static async Task BeginSingleTaskAsync<TResult>(this IServiceProvider serviceProvider,
            Func<TaskCenterSingleContext, Task<TResult>> func,
            Func<Exception, string> failMessageFormatter,
            TaskCenterDto taskCenterModel,
            CancellationToken cancellationToken = default)
        {
            if (taskCenterModel is null)
            {
                throw new ArgumentNullException(nameof(taskCenterModel));
            }
            var taskCenterRecordId = taskCenterModel.TaskCenterRecordId;
            var currentRetryCount = taskCenterModel.CurrentRetryCount;

            var manager = serviceProvider.CreateTaskCenterManager<TResult>(taskCenterModel, isDistributedTask: false, cancellationToken: cancellationToken);
            var context = new TaskCenterSingleContext(taskCenterModel, serviceProvider);

            context.OnUpdate = () =>
            {
                return manager.UpdateProgressAsync(model =>
                {
                    model.ExcutingProgress = context.ExcutingProgress;
                }, false);
            };
            context.SetLoadRequestParams(manager.GetRequestParamsAsync);

            try
            {
                await manager.StartAsync();

                var result = await func(context);

                await manager.SuccessAsync(model =>
                {
                    model.ExcuteResult = result;
                    model.ResultMessage = context.ResultMessage;
                });

            }
            catch (Exception ex)
            {
                var logger = serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger("TaskCenterManager");
                var dict = new Dictionary<string, object>()
                {
                    { "recordId",taskCenterRecordId},
                    { "currentRetryCount",currentRetryCount}
                };

                using (var scope = logger.BeginScope(dict))
                {
                    logger.LogError(ex, $"任务id:{taskCenterRecordId},执行失败:{ex.Message}");
                }

                string message = failMessageFormatter?.Invoke(ex);

                try
                {

                    await manager.FailAsync(ex, message);
                }
                catch (Exception fex)
                {
                    using (var fscope = logger.BeginScope(dict))
                    {
                        logger.LogError(fex, $"任务id:{taskCenterRecordId},执行更新失败信息时失败:{fex.Message}");
                    }
                }

                //throw;
            }
        }
    }
}
